#include "uartdevice.h"

#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <termios.h>
#include <unistd.h>

int pyfree::uart_open( int &fd, const char *name )
{
    //检测串口路径是否存在
    assert( name );
    //以读写形式、不将此终端作为此进程的终端控制器、非阻塞的形式打开串口
    fd = open( name, O_RDWR | O_NOCTTY | O_NDELAY );
    if ( fd == -1 ) {
        char e_buf[64] = { 0 };
        sprintf( e_buf, "uart open %s failed!", name );
        perror( e_buf );
        return -1;
    }
    //设置串口非阻塞，因为这里是以非阻塞形式打开的，第三个参数为0，返回值：成功返回0，失败返回-1，失败原因存入errno
    if ( fcntl( fd, F_SETFL, 0 ) < 0 ) {
        perror( "fcntl failed!" );
        return -1;
    }
    return fd;
};

int pyfree::uart_config( int fd, int baude, int c_flow, int bits, char parity, int stop )
{
    struct termios uart;
    //用于获取termios结构体属性。成功返回0，失败返回非0
    if ( tcgetattr( fd, &uart ) != 0 ) {
        perror( "tcgetattr failed!" );
        return -1;
    }

    switch ( baude ) {
        case 4800:
            cfsetispeed( &uart, B4800 );  //设置输入波特率
            cfsetospeed( &uart, B4800 );  //设置输出波特率
            break;
        case 9600:
            cfsetispeed( &uart, B9600 );
            cfsetospeed( &uart, B9600 );
            break;
        case 19200:
            cfsetispeed( &uart, B19200 );
            cfsetospeed( &uart, B19200 );
            break;
        case 38400:
            cfsetispeed( &uart, B38400 );
            cfsetospeed( &uart, B38400 );
            break;
        default:
            fprintf( stderr, "Unknown baude!" );
            return -1;
    }
    switch ( c_flow ) {
        case 'N':
        case 'n':
            uart.c_cflag &= ~CRTSCTS;  //不进行硬件流控制
            break;
        case 'H':
        case 'h':
            uart.c_cflag |= CRTSCTS;  //进行硬件流控制
            break;
        case 'S':
        case 's':
            uart.c_cflag |= ( IXON | IXOFF | IXANY );  //进行软件流控制
            break;
        default:
            fprintf( stderr, "Unknown c_cflag" );
            return -1;
    }
    switch ( bits ) {
        case 5:
            uart.c_cflag &= ~CSIZE;  //屏蔽其他标志位
            uart.c_cflag |= CS5;     //数据位为5位
            break;
        case 6:
            uart.c_cflag &= ~CSIZE;
            uart.c_cflag |= CS6;
            break;
        case 7:
            uart.c_cflag &= ~CSIZE;
            uart.c_cflag |= CS7;
            break;
        case 8:
            uart.c_cflag &= ~CSIZE;
            uart.c_cflag |= CS8;
            break;
        default:
            fprintf( stderr, "Unknown bits!" );
            return -1;
    }
    switch ( parity ) {
        case 'n':
        case 'N':
            uart.c_cflag &= ~PARENB;  // PARENB：产生奇偶校验
            uart.c_cflag &= ~INPCK;   // INPCK：使奇偶校验起作用
            break;
        case 's':
        case 'S':
            uart.c_cflag &= ~PARENB;
            uart.c_cflag &= ~CSTOPB;  //使用两位停止位
            break;
        case 'o':
        case 'O':
            uart.c_cflag |= PARENB;
            uart.c_cflag |= PARODD;  //使用奇校验
            uart.c_cflag |= INPCK;
            uart.c_cflag |= ISTRIP;  //使字符串剥离第八个字符，即校验位
            break;
        case 'e':
        case 'E':
            uart.c_cflag |= PARENB;
            uart.c_cflag &= ~PARODD;  //非奇校验，即偶校验
            uart.c_cflag |= INPCK;
            uart.c_cflag |= ISTRIP;
            break;
        default:
            fprintf( stderr, "Unknown parity!\n" );
            return -1;
    }
    switch ( stop ) {
        case 1:
            uart.c_cflag &= ~CSTOPB;  // CSTOPB：使用两位停止位
            break;
        case 2:
            uart.c_cflag |= CSTOPB;
            break;
        default:
            fprintf( stderr, "Unknown stop!\n" );
            return -1;
    }

    uart.c_oflag &= ~OPOST;                     // OPOST:表示数据经过处理后输出
    if ( tcsetattr( fd, TCSANOW, &uart ) < 0 )  //激活配置，失败返回-1
    {
        return -1;
    }

    uart.c_lflag &= ~( ICANON | ECHO | ECHOE | ISIG );  //使串口工作在原始模式下
    uart.c_cc[VTIME] = 0;                               //设置等待时间为0
    uart.c_cc[VMIN]  = 1;                               //设置最小接受字符为1
    tcflush( fd, TCIFLUSH );                            //清空输入缓冲区
    if ( tcsetattr( fd, TCSANOW, &uart ) < 0 )          //激活配置
    {
        perror( "tcgetattr failed!" );
        return -1;
    }
    return 0;
}

int pyfree::uart_read( int fd, char *r_buf, int lenth, int time_out_ms /*=1000*/ )
{
    fd_set         rfds;
    struct timeval time;
    ssize_t        cnt = 0;
    /*将读文件描述符加入描述符集合*/
    FD_ZERO( &rfds );
    FD_SET( fd, &rfds );
    /*设置超时为*/
    time.tv_sec  = ( time_t )( time_out_ms / 1000 );
    time.tv_usec = (long)1000 * ( time_out_ms % 1000 );
    /*实现多路IO*/
    int ret = select( fd + 1, &rfds, NULL, NULL, &time );
    switch ( ret ) {
        case -1:
            fprintf( stderr, "select error!\n" );
            break;
        case 0:
            fprintf( stderr, "time over!\n" );
            break;
        default:
            cnt = read( fd, r_buf, lenth );
            if ( cnt == -1 ) {
                fprintf( stderr, "read failed!\n" );
                return -1;
            }
            return cnt;
    }
    return ret;
}

int pyfree::uart_write( int fd, char *r_buf, int lenth, int time_out_ms /*=1000*/ )  //串口写入数据
{
    fd_set         rfds;
    struct timeval time;
    ssize_t        cnt = 0;
    /*将读文件描述符加入描述符集合*/
    FD_ZERO( &rfds );
    FD_SET( fd, &rfds );
    /*设置超时为*/
    time.tv_sec  = ( time_out_ms / 1000 );
    time.tv_usec = 1000 * ( time_out_ms % 1000 );
    /*实现多路IO*/
    int ret = select( fd + 1, &rfds, NULL, NULL, &time );
    switch ( ret ) {
        case -1:
            fprintf( stderr, "select error!\n" );
            break;
        case 0:
            fprintf( stderr, "time over!\n" );
            break;
        default:
            cnt = write( fd, r_buf, lenth );
            if ( cnt != lenth ) {
                fprintf( stderr, "write failed!\n" );
                return -1;
            }
            return cnt;
    }
    return ret;
}

int pyfree::uart_clear( int fd, int model )
{
    assert( fd );
    int ret = tcflush( fd, model );
    if ( ret < 0 ) {
        fprintf( stderr, "tcflush failed!\n" );
    }
    return ret;
}

int pyfree::uart_close( int fd )
{
    assert( fd );  // assert先检查文件描述符是否存在
    close( fd );
    return 0;
}
